home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / nt / source.exe / POSIX / ELVIS / CMD2.C < prev    next >
C/C++ Source or Header  |  1992-09-26  |  18KB  |  897 lines

  1. /* cmd2.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  8.  */
  9.  
  10.  
  11. /* This file contains some of the commands - mostly ones that change text */
  12.  
  13. #include <ctype.h>
  14. #include "config.h"
  15. #include "vi.h"
  16. #include "regexp.h"
  17. #if TOS
  18. # include <stat.h>
  19. #else
  20. # if OSK
  21. #  include "osk.h"
  22. # else
  23. #  include <sys/stat.h>
  24. # endif
  25. #endif
  26.  
  27.  
  28. /*ARGSUSED*/
  29. void cmd_substitute(frommark, tomark, cmd, bang, extra)
  30.     MARK    frommark;
  31.     MARK    tomark;
  32.     CMD    cmd;
  33.     int    bang;
  34.     char    *extra;    /* rest of the command line */
  35. {
  36.     char    *line;    /* a line from the file */
  37.     regexp    *re;    /* the compiled search expression */
  38.     char    *subst;    /* the substitution string */
  39.     char    *opt;    /* substitution options */
  40.     long    l;    /* a line number */
  41.     char    *s, *d;    /* used during subtitutions */
  42.     char    *conf;    /* used during confirmation */
  43.     long    chline;    /* # of lines changed */
  44.     long    chsub;    /* # of substitutions made */
  45.     static    optp;    /* boolean option: print when done? */
  46.     static    optg;    /* boolean option: substitute globally in line? */
  47.     static    optc;    /* boolean option: confirm before subst? */
  48.  
  49.  
  50.     /* for now, assume this will fail */
  51.     rptlines = -1L;
  52.  
  53.     if (cmd == CMD_SUBAGAIN)
  54.     {
  55. #ifndef NO_MAGIC
  56.         if (*o_magic)
  57.             subst = "~";
  58.         else
  59. #endif
  60.         subst = "\\~";
  61.         re = regcomp("");
  62.  
  63.         /* if visual "&", then turn off the "p" and "c" options */
  64.         if (bang)
  65.         {
  66.             optp = optc = FALSE;
  67.         }
  68.     }
  69.     else
  70.     {
  71.         /* make sure we got a search pattern */
  72.         if (*extra != '/' && *extra != '?')
  73.         {
  74.             msg("Usage: s/regular expression/new text/");
  75.             return;
  76.         }
  77.  
  78.         /* parse & compile the search pattern */
  79.         subst = parseptrn(extra);
  80.         re = regcomp(extra + 1);
  81.     }
  82.  
  83.     /* abort if RE error -- error message already given by regcomp() */
  84.     if (!re)
  85.     {
  86.         return;
  87.     }
  88.  
  89.     if (cmd == CMD_SUBSTITUTE)
  90.     {
  91.         /* parse the substitution string & find the option string */
  92.         for (opt = subst; *opt && *opt != *extra; opt++)
  93.         {
  94.             if (*opt == '\\' && opt[1])
  95.             {
  96.                 opt++;
  97.             }
  98.         }
  99.         if (*opt)
  100.         {
  101.             *opt++ = '\0';
  102.         }
  103.  
  104.         /* analyse the option string */
  105.         if (!*o_edcompatible)
  106.         {
  107.             optp = optg = optc = FALSE;
  108.         }
  109.         while (*opt)
  110.         {
  111.             switch (*opt++)
  112.             {
  113.               case 'p':    optp = !optp;    break;
  114.               case 'g':    optg = !optg;    break;
  115.               case 'c':    optc = !optc;    break;
  116.               case ' ':
  117.               case '\t':            break;
  118.               default:
  119.                 msg("Subst options are p, c, and g -- not %c", opt[-1]);
  120.                 return;
  121.             }
  122.         }
  123.     }
  124.  
  125.     /* if "c" or "p" flag was given, and we're in visual mode, then NEWLINE */
  126.     if ((optc || optp) && mode == MODE_VI)
  127.     {
  128.         addch('\n');
  129.         exrefresh();
  130.     }
  131.  
  132.     ChangeText
  133.     {
  134.         /* reset the change counters */
  135.         chline = chsub = 0L;
  136.  
  137.         /* for each selected line */
  138.         for (l = markline(frommark); l <= markline(tomark); l++)
  139.         {
  140.             /* fetch the line */
  141.             line = fetchline(l);
  142.  
  143.             /* if it contains the search pattern... */
  144.             if (regexec(re, line, TRUE))
  145.             {
  146.                 /* increment the line change counter */
  147.                 chline++;
  148.  
  149.                 /* initialize the pointers */
  150.                 s = line;
  151.                 d = tmpblk.c;
  152.  
  153.                 /* do once or globally ... */
  154.                 do
  155.                 {
  156. #ifndef CRUNCH
  157.                     /* confirm, if necessary */
  158.                     if (optc)
  159.                     {
  160.                         for (conf = line; conf < re->startp[0]; conf++)
  161.                             addch(*conf);
  162.                         standout();
  163.                         for ( ; conf < re->endp[0]; conf++)
  164.                             addch(*conf);
  165.                         standend();
  166.                         for (; *conf; conf++)
  167.                             addch(*conf);
  168.                         addch('\n');
  169.                         exrefresh();
  170.                         if (getkey(0) != 'y')
  171.                         {
  172.                             /* copy accross the original chars */
  173.                             while (s < re->endp[0])
  174.                                 *d++ = *s++;
  175.  
  176.                             /* skip to next match on this line, if any */
  177.                             continue;
  178.                         }
  179.                     }
  180. #endif /* not CRUNCH */
  181.  
  182.                     /* increment the substitution change counter */
  183.                     chsub++;
  184.  
  185.                     /* this may be the first line to redraw */
  186.                     redrawrange(l, l + 1L, l + 1L);
  187.  
  188.                     /* copy stuff from before the match */
  189.                     while (s < re->startp[0])
  190.                     {
  191.                         *d++ = *s++;
  192.                     }
  193.     
  194.                     /* substitute for the matched part */
  195.                     regsub(re, subst, d);
  196.                     s = re->endp[0];
  197.                     d += strlen(d);
  198.  
  199.                 } while (optg && regexec(re, s, FALSE));
  200.  
  201.                 /* copy stuff from after the match */
  202.                 while (*d++ = *s++)    /* yes, ASSIGNMENT! */
  203.                 {
  204.                 }
  205.  
  206.                 /* replace the old version of the line with the new */
  207.                 d[-1] = '\n';
  208.                 d[0] = '\0';
  209.                 change(MARK_AT_LINE(l), MARK_AT_LINE(l + 1), tmpblk.c);
  210.  
  211.                 /* if supposed to print it, do so */
  212.                 if (optp)
  213.                 {
  214.                     addstr(tmpblk.c);
  215.                     exrefresh();
  216.                 }
  217.  
  218.                 /* move the cursor to that line */
  219.                 cursor = MARK_AT_LINE(l);
  220.             }
  221.         }
  222.     }
  223.  
  224.     /* tweak for redrawing */
  225.     mustredraw = TRUE;
  226.  
  227.     /* free the regexp */
  228.     free(re);
  229.  
  230.     /* if done from within a ":g" command, then finish silently */
  231.     if (doingglobal)
  232.     {
  233.         rptlines = chline;
  234.         rptlabel = "changed";
  235.         return;
  236.     }
  237.  
  238.     /* Reporting */
  239.     if (chsub == 0)
  240.     {
  241.         msg("Substitution failed");
  242.     }
  243.     else if (chline >= *o_report)
  244.     {
  245.         msg("%ld substitutions on %ld lines", chsub, chline);
  246.     }
  247. }
  248.  
  249.  
  250.  
  251.  
  252. /*ARGSUSED*/
  253. void cmd_delete(frommark, tomark, cmd, bang, extra)
  254.     MARK    frommark;
  255.     MARK    tomark;
  256.     CMD    cmd;
  257.     int    bang;
  258.     char    *extra;
  259. {
  260.     MARK    curs2;    /* an altered form of the cursor */
  261.  
  262.     /* choose your cut buffer */
  263.     if (*extra == '"')
  264.     {
  265.         extra++;
  266.     }
  267.     if (*extra)
  268.     {
  269.         cutname(*extra);
  270.     }
  271.  
  272.     /* make sure we're talking about whole lines here */
  273.     frommark = frommark & ~(BLKSIZE - 1);
  274.     tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
  275.  
  276.     /* yank the lines */
  277.     cut(frommark, tomark);
  278.  
  279.     /* if CMD_DELETE then delete the lines */
  280.     if (cmd != CMD_YANK)
  281.     {
  282.         curs2 = cursor;
  283.         ChangeText
  284.         {
  285.             /* delete the lines */
  286.             delete(frommark, tomark);
  287.         }
  288.         if (curs2 > tomark)
  289.         {
  290.             cursor = curs2 - tomark + frommark;
  291.         }
  292.         else if (curs2 > frommark)
  293.         {
  294.             cursor = frommark;
  295.         }
  296.     }
  297. }
  298.  
  299.  
  300. /*ARGSUSED*/
  301. void cmd_append(frommark, tomark, cmd, bang, extra)
  302.     MARK    frommark;
  303.     MARK    tomark;
  304.     CMD    cmd;
  305.     int    bang;
  306.     char    *extra;
  307. {
  308.     long    l;    /* line counter */
  309.  
  310.     ChangeText
  311.     {
  312.         /* if we're doing a change, delete the old version */
  313.         if (cmd == CMD_CHANGE)
  314.         {
  315.             /* delete 'em */
  316.             cmd_delete(frommark, tomark, cmd, bang, extra);
  317.         }
  318.  
  319.         /* new lines start at the frommark line, or after it */
  320.         l = markline(frommark);
  321.         if (cmd == CMD_APPEND)
  322.         {
  323.              l++;
  324.         }
  325.  
  326.         /* get lines until no more lines, or "." line, and insert them */
  327.         while (vgets('\0', tmpblk.c, BLKSIZE) >= 0)
  328.         {
  329.             addch('\n');
  330.             if (!strcmp(tmpblk.c, "."))
  331.             {
  332.                 break;
  333.             }
  334.  
  335.             strcat(tmpblk.c, "\n");
  336.             add(MARK_AT_LINE(l), tmpblk.c);
  337.             l++;
  338.         }
  339.     }
  340.  
  341.     /* on the odd chance that we're calling this from vi mode ... */
  342.     redraw(MARK_UNSET, FALSE);
  343. }
  344.  
  345.  
  346. /*ARGSUSED*/
  347. void cmd_put(frommark, tomark, cmd, bang, extra)
  348.     MARK    frommark;
  349.     MARK    tomark;
  350.     CMD    cmd;
  351.     int    bang;
  352.     char    *extra;
  353. {
  354.     /* choose your cut buffer */
  355.     if (*extra == '"')
  356.     {
  357.         extra++;
  358.     }
  359.     if (*extra)
  360.     {
  361.         cutname(*extra);
  362.     }
  363.  
  364.     /* paste it */
  365.     ChangeText
  366.     {
  367.         cursor = paste(frommark, TRUE, FALSE);
  368.     }
  369. }
  370.  
  371.  
  372. /*ARGSUSED*/
  373. void cmd_join(frommark, tomark, cmd, bang, extra)
  374.     MARK    frommark;
  375.     MARK    tomark;
  376.     CMD    cmd;
  377.     int    bang;
  378.     char    *extra;
  379. {
  380.     long    l;
  381.     char    *scan;
  382.     int    len;    /* length of the new line */
  383.  
  384.     /* if only one line is specified, assume the following one joins too */
  385.     if (markline(frommark) == nlines)
  386.     {
  387.         msg("Nothing to join with this line");
  388.         return;
  389.     }
  390.     if (markline(frommark) == markline(tomark))
  391.     {
  392.         tomark += BLKSIZE;
  393.     }
  394.  
  395.     /* get the first line */
  396.     l = markline(frommark);
  397.     strcpy(tmpblk.c, fetchline(l));
  398.     len = strlen(tmpblk.c);
  399.  
  400.     /* build the longer line */
  401.     while (++l <= markline(tomark))
  402.     {
  403.         /* get the next line */
  404.         scan = fetchline(l);
  405.  
  406.         /* remove any leading whitespace */
  407.         while (*scan == '\t' || *scan == ' ')
  408.         {
  409.             scan++;
  410.         }
  411.  
  412.         /* see if the line will fit */
  413.         if (strlen(scan) + len + 3 > BLKSIZE)
  414.         {
  415.             msg("Can't join -- the resulting line would be too long");
  416.             return;
  417.         }
  418.  
  419.         /* catenate it, with a space (or two) in between */
  420.         if (len >= 1 &&
  421.             (tmpblk.c[len - 1] == '.'
  422.              || tmpblk.c[len - 1] == '?'
  423.              || tmpblk.c[len - 1] == '!'))
  424.         {
  425.              tmpblk.c[len++] = ' ';
  426.         }
  427.         tmpblk.c[len++] = ' ';
  428.         strcpy(tmpblk.c + len, scan);
  429.         len += strlen(scan);
  430.     }
  431.     tmpblk.c[len++] = '\n';
  432.     tmpblk.c[len] = '\0';
  433.  
  434.     /* make the change */
  435.     ChangeText
  436.     {
  437.         frommark &= ~(BLKSIZE - 1);
  438.         tomark &= ~(BLKSIZE - 1);
  439.         tomark += BLKSIZE;
  440.         change(frommark, tomark, tmpblk.c);
  441.     }
  442.  
  443.     /* Reporting... */
  444.     rptlines = markline(tomark) - markline(frommark) - 1L;
  445.     rptlabel = "joined";
  446. }
  447.  
  448.  
  449.  
  450. /*ARGSUSED*/
  451. void cmd_shift(frommark, tomark, cmd, bang, extra)
  452.     MARK    frommark;
  453.     MARK    tomark;
  454.     CMD    cmd;
  455.     int    bang;
  456.     char    *extra;
  457. {
  458.     long    l;    /* line number counter */
  459.     int    oldidx;    /* number of chars previously used for indent */
  460.     int    newidx;    /* number of chars in the new indent string */
  461.     int    oldcol;    /* previous indent amount */
  462.     int    newcol;    /* new indent amount */
  463.     char    *text;    /* pointer to the old line's text */
  464.  
  465.     /* figure out how much of the screen we must redraw (for vi mode) */
  466.     if (markline(frommark) != markline(tomark))
  467.     {
  468.         mustredraw = TRUE;
  469.         redrawrange(markline(frommark), markline(tomark) + 1L, markline(tomark) + 1L);
  470.     }
  471.  
  472.     ChangeText
  473.     {
  474.         /* for each line to shift... */
  475.         for (l = markline(frommark); l <= markline(tomark); l++)
  476.         {
  477.             /* get the line - ignore empty lines unless ! mode */
  478.             text = fetchline(l);
  479.             if (!*text && !bang)
  480.                 continue;
  481.  
  482.             /* calc oldidx and oldcol */
  483.             for (oldidx = 0, oldcol = 0;
  484.                  text[oldidx] == ' ' || text[oldidx] == '\t';
  485.                  oldidx++)
  486.             {
  487.                 if (text[oldidx] == ' ')
  488.                 {
  489.                     oldcol += 1;
  490.                 }
  491.                 else
  492.                 {
  493.                     oldcol += *o_tabstop - (oldcol % *o_tabstop);
  494.                 }
  495.             }
  496.     
  497.             /* calc newcol */
  498.             if (cmd == CMD_SHIFTR)
  499.             {
  500.                 newcol = oldcol + (*o_shiftwidth & 0xff);
  501.             }
  502.             else
  503.             {
  504.                 newcol = oldcol - (*o_shiftwidth & 0xff);
  505.                 if (newcol < 0)
  506.                     newcol = 0;
  507.             }
  508.  
  509.             /* if no change, then skip to next line */
  510.             if (oldcol == newcol)
  511.                 continue;
  512.  
  513.             /* build a new indent string */
  514.             newidx = 0;
  515.             while (newcol >= *o_tabstop)
  516.             {
  517.                 tmpblk.c[newidx++] = '\t';
  518.                 newcol -= *o_tabstop;
  519.             }
  520.             while (newcol > 0)
  521.             {
  522.                 tmpblk.c[newidx++] = ' ';
  523.                 newcol--;
  524.             }
  525.             tmpblk.c[newidx] = '\0';
  526.             
  527.             /* change the old indent string into the new */
  528.             change(MARK_AT_LINE(l), MARK_AT_LINE(l) + oldidx, tmpblk.c);
  529.         }
  530.     }
  531.  
  532.     /* Reporting... */
  533.     rptlines = markline(tomark) - markline(frommark) + 1L;
  534.     if (cmd == CMD_SHIFTR)
  535.     {
  536.         rptlabel = ">ed";
  537.     }
  538.     else
  539.     {
  540.         rptlabel = "<ed";
  541.     }
  542. }
  543.  
  544.  
  545. /*ARGSUSED*/
  546. void cmd_read(frommark, tomark, cmd, bang, extra)
  547.     MARK    frommark;
  548.     MARK    tomark;
  549.     CMD    cmd;
  550.     int    bang;
  551.     char    *extra;
  552. {
  553.     int    fd, rc;    /* used while reading from the file */
  554.     char    *scan;    /* used for finding NUL characters */
  555.     int    hadnul;    /* boolean: any NULs found? */
  556.     int    addnl;    /* boolean: forced to add newlines? */
  557.     int    len;    /* number of chars in current line */
  558.     long    lines;    /* number of lines in current block */
  559.     struct stat statb;
  560.  
  561.     /* special case: if ":r !cmd" then let the filter() function do it */
  562.     if (extra[0] == '!')
  563.     {
  564.         filter(frommark, MARK_UNSET, extra + 1);
  565.         return;
  566.     }
  567.  
  568.     /* open the file */
  569.     fd = open(extra, O_RDONLY);
  570.     if (fd < 0)
  571.     {
  572.         msg("Can't open \"%s\"", extra);
  573.         return;
  574.     }
  575.  
  576. #ifndef CRUNCH
  577.     if (stat(extra, &statb) < 0)
  578.     {
  579.         msg("Can't stat \"%s\"", extra);
  580.     }
  581. # if TOS
  582.     if (statb.st_mode & S_IJDIR)
  583. # else
  584. #  if OSK
  585.     if (statb.st_mode & S_IFDIR)
  586. #  else
  587.     if ((statb.st_mode & S_IFMT) != S_IFREG)
  588. #  endif
  589. # endif
  590.     {
  591.         msg("\"%s\" is not a regular file", extra);
  592.         return;
  593.     }
  594. #endif /* not CRUNCH */
  595.  
  596.     /* get blocks from the file, and add them */
  597.     ChangeText
  598.     {
  599.         /* insertion starts at the line following frommark */
  600.         tomark = frommark = (frommark | (BLKSIZE - 1L)) + 1L;
  601.         len = 0;
  602.         hadnul = addnl = FALSE;
  603.  
  604.         /* add an extra newline, so partial lines at the end of
  605.          * the file don't trip us up
  606.          */
  607.         add(tomark, "\n");
  608.  
  609.         /* for each chunk of text... */
  610.         while ((rc = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
  611.         {
  612.             /* count newlines, convert NULs, etc. ... */
  613.             for (lines = 0, scan = tmpblk.c; rc > 0; rc--, scan++)
  614.             {
  615.                 /* break up long lines */
  616.                 if (*scan != '\n' && len + 2 > BLKSIZE)
  617.                 {
  618.                     *scan = '\n';
  619.                     addnl = TRUE;
  620.                 }
  621.  
  622.                 /* protect against NUL chars in file */
  623.                 if (!*scan)
  624.                 {
  625.                     *scan = 0x80;
  626.                     hadnul = TRUE;
  627.                 }
  628.  
  629.                 /* starting a new line? */
  630.                 if (*scan == '\n')
  631.                 {
  632.                     /* reset length at newline */
  633.                     len = 0;
  634.                     lines++;
  635.                 }
  636.                 else
  637.                 {
  638.                     len++;
  639.                 }
  640.             }
  641.  
  642.             /* add the text */
  643.             *scan = '\0';
  644.             add(tomark, tmpblk.c);
  645.             tomark += MARK_AT_LINE(lines) + len - markidx(tomark);
  646.         }
  647.  
  648.         /* if partial last line, then retain that first newline */
  649.         if (len > 0)
  650.         {
  651.             msg("Last line had no newline");
  652.             tomark += BLKSIZE; /* <- for the rptlines calc */
  653.         }
  654.         else /* delete that first newline */
  655.         {
  656.             delete(tomark, (tomark | (BLKSIZE - 1L)) + 1L);
  657.         }
  658.     }
  659.  
  660.     /* close the file */
  661.     close(fd);
  662.  
  663.     /* Reporting... */
  664.     rptlines = markline(tomark) - markline(frommark);
  665.     rptlabel = "read";
  666.  
  667.     if (addnl)
  668.         msg("Newlines were added to break up long lines");
  669.     if (hadnul)
  670.         msg("NULs were converted to 0x80");
  671. }
  672.  
  673.  
  674.  
  675. /*ARGSUSED*/
  676. void cmd_undo(frommark, tomark, cmd, bang, extra)
  677.     MARK    frommark;
  678.     MARK    tomark;
  679.     CMD    cmd;
  680.     int    bang;
  681.     char    *extra;
  682. {
  683.     undo();
  684. }
  685.  
  686.  
  687. /* print the selected lines */
  688. /*ARGSUSED*/
  689. void cmd_print(frommark, tomark, cmd, bang, extra)
  690.     MARK    frommark;
  691.     MARK    tomark;
  692.     CMD    cmd;
  693.     int    bang;
  694.     char    *extra;
  695. {
  696.     REG char    *scan;
  697.     REG long    l;
  698.     REG int        col;
  699.  
  700.     for (l = markline(frommark); l <= markline(tomark); l++)
  701.     {
  702.         /* display a line number, if CMD_NUMBER */
  703.         if (cmd == CMD_NUMBER)
  704.         {
  705.             sprintf(tmpblk.c, "%6ld  ", l);
  706.             qaddstr(tmpblk.c);
  707.             col = 8;
  708.         }
  709.         else
  710.         {
  711.             col = 0;
  712.         }
  713.  
  714.         /* get the next line & display it */
  715.         for (scan = fetchline(l); *scan; scan++)
  716.         {
  717.             /* expand tabs to the proper width */
  718.             if (*scan == '\t' && cmd != CMD_LIST)
  719.             {
  720.                 do
  721.                 {
  722.                     qaddch(' ');
  723.                     col++;
  724.                 } while (col % *o_tabstop != 0);
  725.             }
  726.             else if (*scan >= 0 && *scan < ' ' || *scan == '\177')
  727.             {
  728.                 qaddch('^');
  729.                 qaddch(*scan ^ 0x40);
  730.                 col += 2;
  731.             }
  732.             else if ((*scan & 0x80) && cmd == CMD_LIST)
  733.             {
  734.                 sprintf(tmpblk.c, "\\%03o", *scan);
  735.                 qaddstr(tmpblk.c);
  736.                 col += 4;
  737.             }
  738.             else
  739.             {
  740.                 qaddch(*scan);
  741.                 col++;
  742.             }
  743.  
  744.             /* wrap at the edge of the screen */
  745.             if (!has_AM && col >= COLS)
  746.             {
  747.                 addch('\n');
  748.                 col -= COLS;
  749.             }
  750.         }
  751.         if (cmd == CMD_LIST)
  752.         {
  753.             qaddch('$');
  754.         }
  755.         addch('\n');
  756.         exrefresh();
  757.     }
  758. }
  759.  
  760.  
  761. /* move or copy selected lines */
  762. /*ARGSUSED*/
  763. void cmd_move(frommark, tomark, cmd, bang, extra)
  764.     MARK    frommark;
  765.     MARK    tomark;
  766.     CMD    cmd;
  767.     int    bang;
  768.     char    *extra;
  769. {
  770.     MARK    destmark;
  771.  
  772.     /* parse the destination linespec.  No defaults.  Line 0 is okay */
  773.     destmark = cursor;
  774.     if (!strcmp(extra, "0"))
  775.     {
  776.         destmark = 0L;
  777.     }
  778.     else if (linespec(extra, &destmark) == extra || !destmark)
  779.     {
  780.         msg("invalid destination address");
  781.         return;
  782.     }
  783.  
  784.     /* flesh the marks out to encompass whole lines */
  785.     frommark &= ~(BLKSIZE - 1);
  786.     tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
  787.     destmark = (destmark & ~(BLKSIZE - 1)) + BLKSIZE;
  788.  
  789.     /* make sure the destination is valid */
  790.     if (cmd == CMD_MOVE && destmark >= frommark && destmark < tomark)
  791.     {
  792.         msg("invalid destination address");
  793.     }
  794.  
  795.     /* Do it */
  796.     ChangeText
  797.     {
  798.         /* save the text to a cut buffer */
  799.         cutname('\0');
  800.         cut(frommark, tomark);
  801.  
  802.         /* if we're not copying, delete the old text & adjust destmark */
  803.         if (cmd != CMD_COPY)
  804.         {
  805.             delete(frommark, tomark);
  806.             if (destmark >= frommark)
  807.             {
  808.                 destmark -= (tomark - frommark);
  809.             }
  810.         }
  811.  
  812.         /* add the new text */
  813.         paste(destmark, FALSE, FALSE);
  814.     }
  815.  
  816.     /* move the cursor to the last line of the moved text */
  817.     cursor = destmark + (tomark - frommark) - BLKSIZE;
  818.     if (cursor < MARK_FIRST || cursor >= MARK_LAST + BLKSIZE)
  819.     {
  820.         cursor = MARK_LAST;
  821.     }
  822.  
  823.     /* Reporting... */
  824.     rptlabel = ( (cmd == CMD_COPY) ? "copied" : "moved" );
  825. }
  826.  
  827.  
  828.  
  829. /* execute EX commands from a file */
  830. /*ARGSUSED*/
  831. void cmd_source(frommark, tomark, cmd, bang, extra)
  832.     MARK    frommark;
  833.     MARK    tomark;
  834.     CMD    cmd;
  835.     int    bang;
  836.     char    *extra;
  837. {
  838.     /* must have a filename */
  839.     if (!*extra)
  840.     {
  841.         msg("\"source\" requires a filename");
  842.         return;
  843.     }
  844.  
  845.     doexrc(extra);
  846. }
  847.  
  848.  
  849. #ifndef NO_AT
  850. /*ARGSUSED*/
  851. void cmd_at(frommark, tomark, cmd, bang, extra)
  852.     MARK    frommark;
  853.     MARK    tomark;
  854.     CMD    cmd;
  855.     int    bang;
  856.     char    *extra;
  857. {
  858.     static    nest = FALSE;
  859.     int    result;
  860.     char    buf[MAXRCLEN];
  861.  
  862.     /* don't allow nested macros */
  863.     if (nest)
  864.     {
  865.         msg("@ macros can't be nested");
  866.         return;
  867.     }
  868.     nest = TRUE;
  869.  
  870.     /* require a buffer name */
  871.     if (*extra == '"')
  872.         extra++;
  873.     if (!*extra || !isascii(*extra) ||!islower(*extra))
  874.     {
  875.         msg("@ requires a cut buffer name (a-z)");
  876.     }
  877.  
  878.     /* get the contents of the buffer */
  879.     result = cb2str(*extra, buf, (unsigned)(sizeof buf));
  880.     if (result <= 0)
  881.     {
  882.         msg("buffer \"%c is empty", *extra);
  883.     }
  884.     else if (result >= sizeof buf)
  885.     {
  886.         msg("buffer \"%c is too large to execute", *extra);
  887.     }
  888.     else
  889.     {
  890.         /* execute the contents of the buffer as ex commands */
  891.         exstring(buf, result);
  892.     }
  893.  
  894.     nest = FALSE;
  895. }
  896. #endif
  897.